package com.puff.ioc;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.puff.framework.annotation.Transaction;
import com.puff.framework.utils.ClassUtil;

public class JavaSourceCreate {

	private static Map<String, String> map = new HashMap<String, String>();
	static {
		map.put("boolean", "Boolean");
		map.put("short", "Short");
		map.put("int", "Integer");
		map.put("long", "Long");
		map.put("double", "Double");
		map.put("byte", "Byte");
		map.put("float", "Float");
		map.put("char", "Char");
		map.put("String", "String");
	}

	public static JavaSource enhance(Class<?> klass) {
		StringBuffer java = new StringBuffer(1024);
		java.append(klass.getPackage());
		java.append(";");
		java.append("import java.sql.Connection;");
		java.append("import com.puff.exception.ExceptionUtil;");
		java.append("import com.puff.jdbc.core.DataBase;");
		java.append("import com.puff.jdbc.core.DbManager;");
		java.append("public final class ");
		java.append(klass.getSimpleName());
		java.append("$Fast");
		java.append(" extends ");
		java.append(klass.getName());
		java.append("{");
		java.append(crateMethod(klass));
		java.append("}");
		JavaSource src = new JavaSource();
		src.setPackageName(klass.getPackage().getName());
		src.setSimpleName(klass.getSimpleName() + "$Fast");
		src.setSource(java.toString());
		return src;
	}

	public static StringBuffer crateMethod(Class<?> klass) {
		List<Method> methods = ClassUtil.getMethod(klass, new ArrayList<Method>());
		StringBuffer sb = new StringBuffer();

		for (Method method : methods) {
			Transaction tran = method.getAnnotation(Transaction.class);
			String dataSource = "";
			if (tran == null) {
				continue;
			}
			if (Modifier.isFinal(method.getModifiers())) {
				throw new IllegalArgumentException("@Transaction Method can not be final," + method.toString());
			}

			if (Modifier.isAbstract(method.getModifiers())) {
				throw new IllegalArgumentException("@Transaction Method can not be abstract," + method.toString());
			}

			if (Modifier.isPrivate(method.getModifiers())) {
				throw new IllegalArgumentException("@Transaction Method can not be private," + method.toString());
			}

			if (Modifier.isStatic(method.getModifiers())) {
				throw new IllegalArgumentException("@Transaction Method can not be static," + method.toString());
			}

			dataSource = tran.value();
			sb.append("@Override ");
			sb.append(method.toString().split(method.getDeclaringClass().getName() + "." + method.getName())[0]);
			sb.append(method.getName());
			sb.append("(");
			Type[] paramTypeList = method.getParameterTypes();// 方法的参数列表
			int i = 1;
			StringBuffer param = new StringBuffer();
			for (Type paramType : paramTypeList) {
				if (i != 1) {
					sb.append(", ");
					param.append(", ");
				}
				String key = warperParam(paramType, paramType.toString());
				if (key.startsWith("class ")) {
					key = key.substring(6);
				}
				if (key.startsWith("interface ")) {
					key = key.substring(10);
				}
				sb.append(key);
				sb.append(" ");
				sb.append("param" + i);
				param.append("param" + i++);
			}
			sb.append(")");

			Class<?>[] exceptionTypes = method.getExceptionTypes();
			if (exceptionTypes != null && exceptionTypes.length > 0) {
				sb.append(" throws ");
				i = 1;
				for (Class<?> paramType : exceptionTypes) {
					if (i++ != 1) {
						sb.append(", ");
					}
					sb.append(paramType.getName());
				}
			}

			String typeName = method.getReturnType().toString();
			boolean hasReturnType = !"void".equals(typeName);
			sb.append("{");
			if (hasReturnType) {
				String warper = warper(method.getGenericReturnType(), typeName);
				if (warper.startsWith("class ")) {
					warper = warper.substring(6);
				}
				if (warper.startsWith("interface ")) {
					warper = warper.substring(10);
				}
				sb.append(warper).append(" result = null;");
			}
			sb.append("java.sql.Connection conn = com.puff.jdbc.core.DataBase.getThreadLocalConnection();");
			sb.append("if (conn != null) {");
			if (hasReturnType) {
				sb.append("return ");
			}
			sb.append("super.").append(method.getName());
			sb.append("(");
			sb.append(param);
			sb.append(");");
			sb.append("}else{");
			sb.append("java.lang.Boolean autoCommit = null;");
			sb.append("try {");
			sb.append("conn = com.puff.jdbc.core.DbManager.getConnection(\"" + dataSource + "\");");
			sb.append("autoCommit = conn.getAutoCommit();");
			sb.append("com.puff.jdbc.core.DataBase.setThreadLocalConnection(conn);");
			sb.append("conn.setAutoCommit(false);");

			if (hasReturnType) {
				sb.append("result = ");
			}
			sb.append("super.").append(method.getName());
			sb.append("(");
			sb.append(param);
			sb.append(");");
			sb.append("conn.commit();");
			sb.append("} catch (Throwable t) {");
			sb.append("if (conn != null)try {conn.rollback();} catch (Exception e) {}com.puff.exception.ExceptionUtil.throwRuntime(t);");
			sb.append("} finally {");
			sb.append(
					"try {if (conn != null) {if (autoCommit != null)conn.setAutoCommit(autoCommit);conn.close();}} catch (Throwable t) {} finally {com.puff.jdbc.core.DataBase.removeThreadLocalConnection();}");
			sb.append("}");
			sb.append("}");
			if (hasReturnType) {
				sb.append("return result;");
			}
			sb.append("}");
		}
		return sb;
	}

	@SuppressWarnings("rawtypes")
	private static String warperParam(Type type, String typeName) {
		if (type instanceof Class) {
			Class c = (Class) type;
			boolean array = c.isArray();
			if (array) {
				Class class1 = c.getComponentType();
				boolean containsKey = map.containsKey(class1.getSimpleName());
				if (containsKey) {
					return class1.getSimpleName() + "[]";
				} else {
					return class1.getName() + "[]";
				}
			}
		}
		return typeName;
	}

	@SuppressWarnings("rawtypes")
	private static String warper(Type type, String typeName) {
		if (type instanceof Class) {
			Class c = (Class) type;
			boolean array = c.isArray();
			if (array) {
				Class class1 = c.getComponentType();
				boolean containsKey = map.containsKey(class1.getSimpleName());
				if (containsKey) {
					return class1.getSimpleName() + "[]";
				}
			}
		}
		String result = map.get(typeName);
		return result == null ? typeName : result;
	}

}
